/***************************************************************************
 *   Copyright (C) 2008 by Jesse Crossen   *
 *   jesse.crossen@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "tuner.h"

Tuner::Tuner(QWidget *parent)
 : QWidget(parent)
{
  // set defaults
  currentInstrument = 0;
  currentTranspose = 0;
  currentSensitivity = 50;

  // initialize the music object
  music = new Music();
  // rebuild the panel if the scale changes
  connect(music, SIGNAL(scaleChanged()),
          this, SLOT(buildPanel()));
  // initialize the audio listener
  listener = new Listener();
  // make a vertical stack of two grids, 
  //  one for options and another for strobes
  QVBoxLayout * layout = new QVBoxLayout;
  QGridLayout * options = new QGridLayout;
  strobeGrid = new QGridLayout;
  layout->addLayout(options);
  layout->addLayout(strobeGrid);
  setLayout(layout);
  
  // allow the right column to stretch
  options->setColumnStretch(1, 1);
  // add options
  int row = 0;
  // input device
  QLabel * deviceLabel = new QLabel("input device");
  QComboBox * deviceCombo = new QComboBox;
  listener->FillDeviceCombo(deviceCombo);
  connect(deviceCombo, SIGNAL(activated(int)),
          listener, SLOT(setDevice(int)));
  options->addWidget(deviceLabel, row, 0, Qt::AlignRight);
  options->addWidget(deviceCombo, row++, 1);
  // reference note
  QLabel * refLabel = new QLabel("frequency");
  QDoubleSpinBox * refSpinBox = new QDoubleSpinBox;
  refSpinBox->setRange(1.0, 10000.0);
  refSpinBox->setSingleStep(1.0);
  refSpinBox->setValue(440.0);
  refSpinBox->setDecimals(1);
  connect(refSpinBox, SIGNAL(valueChanged(double)),
          music, SLOT(setReferenceFrequency(double)));
  options->addWidget(refLabel, row, 0, Qt::AlignRight);
  options->addWidget(refSpinBox, row++, 1);
  // instrument being tuned
  QLabel * instrumentLabel = new QLabel("instrument");
  QComboBox * instrumentCombo = new QComboBox;
  music->FillInstrumentCombo(instrumentCombo);
  connect(instrumentCombo, SIGNAL(activated(int)),
          this, SLOT(setInstrument(int)));
  options->addWidget(instrumentLabel, row, 0, Qt::AlignRight);
  options->addWidget(instrumentCombo, row++, 1);
  // capo position
  QLabel * capoLabel = new QLabel("transpose/capo");
  QSpinBox * capoSpinBox = new QSpinBox;
  connect(capoSpinBox, SIGNAL(valueChanged(int)),
          this, SLOT(setTranspose(int)));
  options->addWidget(capoLabel, row, 0, Qt::AlignRight);
  options->addWidget(capoSpinBox, row++, 1);
  capoSpinBox->setRange(-96, 96);
  // sensitivity
  QLabel * sensitivityLabel = new QLabel("sensitivity");
  QSlider * sensitivitySlider = new QSlider(Qt::Horizontal);
  connect(sensitivitySlider, SIGNAL(valueChanged(int)),
          this, SLOT(setSensitivity(int)));
  options->addWidget(sensitivityLabel, row, 0, Qt::AlignRight);
  options->addWidget(sensitivitySlider, row++, 1);
  sensitivitySlider->setRange(0, 100);
  sensitivitySlider->setTickInterval(10);
  sensitivitySlider->setValue(currentSensitivity);
  
  // set up the panel
  buildPanel();
  // start listening to audio
  listener->Listen();
}


Tuner::~Tuner()
{
}

// set up the instrument panel
void Tuner::buildPanel() {
  int i, r, s;
  // clear the strobe grid
  for(i = 0; i < strobes.size(); i++) {
    strobeGrid->removeWidget(strobes.at(i));
  }
  // start adding strobes out of the pool at this index
  s = 0;
  // get the current instrument setup
  Music::Instrument inst = music->Instruments[currentInstrument];
  // add rows of strobes
  QList<int> row;
  for(r = 0; r < inst.strobeNotes.size(); r++) {
    row = inst.strobeNotes.at(r);
    // add cells from the row
    for(i = 0; i < row.size(); i++) {
      // get the note index at this position, and offset by the transpose factor
      int note = row.at(i) + currentTranspose;
      // bounds check
      if (note < 0) note = 0;
      if (note > music->Frequencies.size()) note = music->Frequencies.size() - 1;
      // set up the strobe
      Strobe * strobe;
      // make sure we have a strobe at this position
      if (s >= strobes.size()) {
        strobe = new Strobe();
        strobes << strobe;
        // make it listen to audio
        connect(listener, SIGNAL(audioHeard(const float *, unsigned long, float, float)),
                strobe, SLOT(addAudio(const float *, unsigned long, float, float)));
      }
      else strobe = strobes.at(s);
      strobe->setNoteFrequency(music->Frequencies[note]);
      strobe->setNoteName(music->NoteName(note));
      // high contrast means low sensitivity
      strobe->setContrast(1.0 - ((float)currentSensitivity / 100.0));
      // add the strobe into the grid
      strobeGrid->addWidget(strobe, r, i);
      strobe->show();
      // step through the strobe pool
      s++;
    }
  }
  // hide unused strobes
  for(i = s; i < strobes.size(); i++) {
    strobes[i]->hide();
  }
}

// set the instrument being tuned
void Tuner::setInstrument(int index) {
  if (index < 0) index = 0;
  if (index >= music->Instruments.size()) index = music->Instruments.size() - 1;
  if (index != currentInstrument) {
    currentInstrument = index;
    buildPanel();
  }
}

// set how much to transpose the current instrument
void Tuner::setTranspose(int semitones) {
  if (semitones != currentTranspose) {
    currentTranspose = semitones;
    buildPanel();
  }
}

// set the display sensitivity as a percentage between 0 and 100
void Tuner::setSensitivity(int percent) {
  if (percent < 0) percent = 0;
  if (percent > 100) percent = 100;
  if (percent != currentSensitivity) {
    currentSensitivity = percent;
    buildPanel();
  }
}


